package org.kepler.chat.api.controller;

import io.swagger.annotations.ApiOperation;
import org.apache.commons.codec.digest.DigestUtils;
import org.bigdata.frame.exception.BizException;
import org.bigdata.frame.util.ReturnDatas;
import org.kepler.chat.api.entity.AuthUser;
import org.kepler.chat.api.infra.constant.Constants;
import org.kepler.chat.api.infra.security.auth.jwt.extractor.TokenExtractor;
import org.kepler.chat.api.infra.security.auth.jwt.verifier.TokenVerifier;
import org.kepler.chat.api.infra.security.config.JwtSettings;
import org.kepler.chat.api.infra.security.config.WebSecurityConfig;
import org.kepler.chat.api.infra.security.model.token.*;
import org.kepler.chat.api.infra.security.model.UserContext;
import org.kepler.chat.api.infra.util.ErrorCode;
import org.kepler.loan.api.service.CustomUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * RefreshTokenEndpoint
 *
 * @author ake
 * <p>
 * Aug 17, 2016
 */
@RestController
@RequestMapping(value = "/rest/auth")
public class AuthApi extends BaseApi {
    @Autowired
    private JwtTokenFactory tokenFactory;
    @Autowired
    private JwtSettings jwtSettings;
    @Autowired
    private TokenVerifier tokenVerifier;
    @Autowired
    @Qualifier("jwtHeaderTokenExtractor")
    private TokenExtractor tokenExtractor;
    @Autowired
    private CustomUserService customUserService;

    @ApiOperation(value = "根据账号和密码生成token", notes = "生成新的token")
    @RequestMapping(value = "/create", method = RequestMethod.POST, produces = {MediaType.APPLICATION_JSON_VALUE})
    public ReturnDatas createAccessJwtToken(HttpServletRequest request
            , @RequestParam("username") String username
            , @RequestParam("password") String password
    ) {
        String loginFlagUserName = Constants.LOGINFLAG_USER + "-" + username;
        AuthUser authUser = (AuthUser) customUserService.loadUserByUsername(loginFlagUserName);
        if (null == authUser) {
            throw new BizException(ErrorCode.USER_NOT_EXIST.getCode(), ErrorCode.USER_NOT_EXIST.getMessage());
        }
        if (!authUser.getPassword().equals(DigestUtils.md5Hex(password))) {
            throw new BizException(ErrorCode.PARM_PASSWORD_ERROR.getCode(), ErrorCode.PARM_PASSWORD_ERROR.getMessage());
        }
        List<GrantedAuthority> authorities = new ArrayList<>();
        UserContext userContext = UserContext.create(username, authorities);
        userContext.setUserId(authUser.getId());
        JwtToken accessToken = tokenFactory.createAccessJwtToken(userContext);

        ReturnDatas returnDatas = ReturnDatas.getSuccessReturnDatas();
        Map<String, Object> tokenMap = new HashMap<String, Object>();
        tokenMap.put(WebSecurityConfig.AUTHENTICATION_HEADER_NAME, accessToken.getToken());
        tokenMap.put("userId", userContext.getUserId());
        returnDatas.setData(tokenMap);

        return returnDatas;
    }

    @ApiOperation(value = "刷新token", notes = "获取新的token")
    @RequestMapping(value = "/token", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE})
    public ReturnDatas refreshToken(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        String tokenPayload = tokenExtractor.extract(request.getHeader(WebSecurityConfig.AUTHENTICATION_HEADER_NAME));

        RawAccessJwtToken rawToken = new RawAccessJwtToken(tokenPayload);
        RefreshToken refreshToken = RefreshToken.create(rawToken, jwtSettings.getTokenSigningKey())
                .orElseThrow(() -> new BizException(ErrorCode.API_INVALID_TOKEN.getCode(), ErrorCode.API_INVALID_TOKEN.getMessage()));

        String jti = refreshToken.getJti();
        if (!tokenVerifier.verify(jti)) {
            throw new BizException(ErrorCode.API_INVALID_TOKEN.getCode(), ErrorCode.API_INVALID_TOKEN.getMessage());
        }

        String subject = refreshToken.getSubject();
        UserDetails user = customUserService.loadUserByUsername(subject);
        if (null == user) {
            throw new BizException(ErrorCode.USER_NOT_EXIST.getCode(), ErrorCode.USER_NOT_EXIST.getMessage());
        }

//        if (user.getRoles() == null) throw new InsufficientAuthenticationException("User has no roles assigned");
//        List<GrantedAuthority> authorities = user.getRoles().stream()
//                .map(authority -> new SimpleGrantedAuthority(authority.getRole().authority()))
//                .collect(Collectors.toList());
        List<GrantedAuthority> authorities = new ArrayList<>();

        UserContext userContext = UserContext.create(user.getUsername(), authorities);
        AccessJwtToken accessJwtToken = tokenFactory.createAccessJwtToken(userContext);

        ReturnDatas returnDatas = ReturnDatas.getSuccessReturnDatas();
        returnDatas.setData(accessJwtToken);

        return returnDatas;
    }
}
